package com.qp.basics.estivate.support;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.ArrayList;
import java.util.List;

public class SqlUtil {
	static Log log = LogFactory.getLog(SqlUtil.class);

	/**
	 * 逻辑按行分析，如果上一个语句还未结束，而开头是#,-(--)，/(//)，/(/*)则认为是注释， 如果是行注释，则直接循环到行结束
	 * 如果是段注释，循环到*\/为止
	 * 如果是'',""已成对的结束，这时出现#,--,//，/*，则认为注释开始，前三者直接换读下一行，后者找寻*\/(转义)
	 * 如果'',""已成对出现，这时出现了";"，则认为该sql语句结束，保存，开始分析下一条。
	 * 
	 * restrict 记不起加这个参数是准备做什么的了。
	 * 
	 * @param sqls
	 * @return
	 */
	public static String[] simpleSqlsSeparating(String sqls, int restrict) {

		String[] sqlsArray;
		List sqlsList = new ArrayList();
		try {
			int length = sqls.length();
			char[] sqlsChars = sqls.toCharArray();
			int i = 0;
			// 本来是想列出行数的，这样在报告语法错误的时候可以制定行数，但似无必要，
			// 因为可以提供其他信息来帮助检查
			int lineCount = 0;
			// 首先是扫除sql语句前的东西，然后是开始采摘sql，并且过滤其中的注释
			// 遇到';'说明sql结束

			// 过滤sql前内容的策略是遇到注释跳过，遇到空格跳过，遇到其它非字母的东西跳过。
			while (i < length) {

				// 如果开头
				// 不是字母，又不是注释，又不是可以忽略的空格或tab，又不是换行，就搞不清是什么东西了
				// 能做的就是忽略掉，或者是抛出异常
				// 而注释又分为
				while (i < length) {
					char c = sqlsChars[i];
					if (c < 'A' || c > 'z') {
						// 如果不是字母说明可能是注释之类的
						if (c == '-' || (c == '/' && sqlsChars[i + 1] == '/')
								|| c == '#') {// 如果是行注释
							while (++i < length && sqlsChars[i] != '\n')
								;
							i++;
							continue;
						} else if ((c == '/' && sqlsChars[i + 1] == '*')) {// 如果是段注释
							// 这里没有防止\\*/这种结束情况的出现，如果文字到\*就结束了，也会越界
							while (true)
								if (sqlsChars[++i] == '/'
										&& sqlsChars[i - 1] == '*'
										&& sqlsChars[i - 2] != '\\')
									break;
							i++;
							continue;
						} else if (c == '\t' || c == ' ') {// 如果是tab或空格则行进到不是tab空格和的字符
							while (++i < length
									&& (sqlsChars[i] == '\t' || sqlsChars[i] == ' '))
								;
							i++;
							continue;
						} else if (c == '\r' || c == '\n') {// 如果是换行符，就继续走
							i++;
							continue;
						} else {
							if (restrict > 0)
								throw new IllegalArgumentException(
										"invalid line start char '" + c + "'."
												+ "i=" + i);
							while (++i < length && sqlsChars[i] != '\n')
								;
							i++;
							continue;
						}

					} else {
						break;
					}
				}
				// 一个sql语句应该有以下部分，
				// 操作符，字段，表明等，数据，引号之间的应该都是连续的数据。也就是说
				// 注释不应该出现在引号之间，而引号之间也不应该有可能的数据，
				// 再换句话说就是遇到引号就录入整个引号段，遇到注释就跳过整个注释
				// 对于可能表示语句结束的')'，也有类似的逻辑

				// 以上是扫除sql语句前的妖魔鬼怪，来到sql的门前，
				// 在进入sql语句之后，在成对的包围符（'',""）之外也可能会遇到注释，如果是注释就跳过，否则就加起来
				// 如果遇到';'说明结束，另外如果遇到')'，并且整个字符串结束也说明是结束。
				StringBuffer sql = new StringBuffer();// 一条sql语句

				while (i < length) {
					char c = sqlsChars[i];
					// 如果是首次遇到引号，就将引号内的所有东西读完
					if (c == '\'' || c == '\"') {
						char quote = c;
						sql.append(c);
						while (i < length) {
							c = sqlsChars[++i];// 取下一个字符
							if (c == '\\') {// 如果是转义符则连同其后的一个字符一起被认为是普通字符
								sql.append(c);
								sql.append(sqlsChars[++i]);
							} else if (c == '\'' || c == '\"') {
								sql.append(c);
								if (c == quote) {
									if (i + 1 < length)
										c = sqlsChars[++i];// 因为是break，而前一个while里没有++i，所以这里要++
									break;
								}
							} else {
								sql.append(c);
							}
							continue;
						}

					}
					// 这里只接受//，/**/ 和 #这种注释
					if ((c == '/' && sqlsChars[i + 1] == '/') || c == '#') {// 如果是行注释
						while (++i < length && sqlsChars[i] != '\n')
							;
						i++;
						continue;
					} else if ((c == '/' && sqlsChars[i + 1] == '*')) {// 如果是段注释
						// 这里没有防止\\*/这种结束情况的出现，如果文字到\*就结束了，也会越界
						while (true)
							if (sqlsChars[++i] == '/'
									&& sqlsChars[i - 1] == '*'
									&& sqlsChars[i - 2] != '\\')
								break;
						i++;
						continue;
					}
					if (c == ';') {
						i++;
						break;
						// 结束了，因为';'之可能出现在注释和引号对了，现在不是在引号对里，也不是在注释里
						// 所以一定是结束了
					}
					// 如果不是以上任何一种情况
					sql.append(c);
					i++;
				}// while (i < length)
				sqlsList.add(sql);
			}
			sqlsArray = new String[sqlsList.size()];
			sqlsList.toArray(sqlsArray);
			return sqlsArray;
		} catch (Exception e) {
			log.error(e.getMessage(), e);

			throw new RuntimeException(e.getMessage(), e);
		}

	}

}
